/**
 * Copyright (c) 2011 Technical Centre Radio Systems Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ua.tcrs.tabula.container;

import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Method;
import java.util.concurrent.CopyOnWriteArrayList;

import ua.tcrs.tabula.Dispatcher;
import ua.tcrs.tabula.Listener;
import ua.tcrs.tabula.ListenerInterceptor;
import ua.tcrs.tabula.Topic;
import ua.tcrs.tabula.Subscribe;
import ua.tcrs.tabula.Subscription;
import ua.tcrs.tabula.Event;
import ua.tcrs.tabula.impl.MethodListener;
import ua.tcrs.tabula.util.Reflect;

public class SimpleDispatcher implements Dispatcher {	
	final private static CopyOnWriteArrayList<SubscriptionImpl> subscriptions = new CopyOnWriteArrayList<SubscriptionImpl>();
	
	private static boolean active = false;
	public static void stop() {active = false;}
	public static void start() {active = true;}
	public static boolean isActive() {return active;}
	
	public <I> Subscription subscribe( Topic<I> t, Listener<I,?> l ) {
		SubscriptionImpl ds = new SubscriptionImpl(t, l);
		subscriptions.add(ds);
		return ds;
	}
	public List<Subscription> subscribeAnnotated( Object target ) {
		if ( target == null) throw new IllegalArgumentException();
		
		List<Subscription> result = new ArrayList<Subscription>();
		for ( Method method: Reflect.allAnnotatedMethods( target.getClass(), Subscribe.class ) ) {
			Subscribe s = method.getAnnotation( Subscribe.class );
			result.add( subscribe( new Topic<Object>(s.group(), s.event()), new MethodListener( target, method ) ) );
		}
		return result;
	}
	public boolean unsubscribe( Subscription s ) {
		return subscriptions.remove( s );
	}
	public boolean unsubscribe( List<Subscription> ss ) {
		return subscriptions.removeAll( ss );
	}
	
	public <U> List<U> publish( Event<?,?> evt) {
		List<U> results = new ArrayList<U>();
		if ( isActive() ) {
			for ( SubscriptionImpl sub : subscriptions ) {
				if ( !sub.isPaused() && sub.getTopic().match(evt) ) {
					@SuppressWarnings("unchecked")
					U result = (U)((Listener)sub.getHandler()).onEvent( evt );
					if ( result != null) { results.add( result ); }
					if ( Thread.currentThread().isInterrupted() ) break;
				}
			}
		}
		return results;
	}
	
	static class SubscriptionImpl implements Subscription {
		class InterceptedListenerDecorator implements Listener<Object,Object> {
			Listener<Object,Object> target;
			ListenerInterceptor<Object,Object> interceptor;
			@SuppressWarnings("unchecked")
			InterceptedListenerDecorator( Listener<?,?> target, ListenerInterceptor<?,?> interceptor ) {
				this.target = (Listener<Object,Object>)target;
				this.interceptor = (ListenerInterceptor<Object,Object>)interceptor;
			}
			public Object onEvent( Event<Object,Object> event ) {
				return interceptor.onEvent( target, event );
			}
		}
		private boolean paused;
		private Topic<?> topic;
		private Listener<?,?> handler;
		public SubscriptionImpl( Topic<?> topic, Listener<?,?> handler ) {
			this.paused = false;
			this.topic = topic;
			this.handler = handler;
		}
		public Topic<?> getTopic() { return topic; }
		public Listener<?,?> getHandler() { return handler; }
		public void addInterceptor( ListenerInterceptor<?,?> interceptor ) {
			handler = new InterceptedListenerDecorator( handler, interceptor );
		}
		public boolean isPaused() { return paused; }
		public void pause() { paused = true; }
		public void resume() { paused = false; }
	}
}
